module.exports = {
    /* 
        1. 新建计算器
        2. 搬移 play 字段
        3. 把函数搬移进计算器
        4. 使计算器表现出多态性
    */
   

    createStatementData(invoice, plays) {
        
        /****************************************** */
        class PerformanceCalculator {
            constructor(aPerformance, aPlay) {
                this.performance = aPerformance;
                this.play = aPlay;
            }

            amount() {
              throw new Error("subclass responsibility");
            }

            volumeCredits() {
                return Math.max(this.performance.audience -30,0);
            }
        }

        class TragedyCalculator  extends PerformanceCalculator{
            amount(){
                let result = 0;
                switch (this.play.type) {
                    case "tragedy":
                        result = 40000;
                        if (this.performance.audience > 30) {
                            result += 1000 * (this.performance.audience - 30);
                        }
                        break;
                    case "comedy":
                        throw "bad thing";
                    default:
                        throw new Error("unknown type:" + this.performance.play.type);
                }
                return result;
            }
        }
        class ComedyCalculator extends PerformanceCalculator{
            amount(){
                let result = 0;
                switch (this.play.type) {
                    case "comedy":
                        result = 3000;
                        if (this.performance.audience > 20) {
                            result += 1000 + 500 * (this.performance.audience - 20);
                        }
                        break;
                    case "tragedy":
                        throw "bad thing";
                    default:
                        throw new Error("unknown type:" + this.performance.play.type);
                }
                return result;
            }

            volumeCredits(){
                return super.volumeCredits() + Math.floor(this.performance.audience / 5);
            }
        }

        function createPerformanceCalculator(aPerformance,aPlay) {
            switch(aPlay.type){
                case "tragedy":
                    return new TragedyCalculator(aPerformance,aPlay);
                case "comedy":
                    return new ComedyCalculator(aPerformance,aPlay);
                default:
                    throw new Error("unknow type:" + aPlay.type);
            }
        }

        /****************************************** */
        const statementData = {};
        statementData.customer = invoice.customer;
        // map() 方法通过对每个数组元素执行函数来创建新数组。
        statementData.performance = invoice.performance.map(enrichPerformance);
        statementData.totalAmount = totalAmount(statementData);
        statementData.totalVolumeCredits = totalVolumeCredits(statementData);
        return statementData;

        /* 
            返回一个浅副本
            Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象身上
        */
        function enrichPerformance(aPerformance) {
            const calculator = createPerformanceCalculator(aPerformance,playFor(aPerformance));
            // Object.assign(target, source)
            const result = Object.assign({}, aPerformance);
            result.play = calculator.play;
            result.amount = calculator.amount();
            result.volumeCredits = calculator.volumeCredits();
            return result;
        }

        function playFor(aPerformance) {
            return plays[aPerformance.playID];
        }

        function totalAmount(data) {
            let result = 0;
            for (let perf of data.performance) {
                result += perf.amount;
            };
            return result;

            // 用管道取代循环
            // return data.performance.reduce((total,p)=>{total+p.amount,0});
        }
        function totalVolumeCredits(data) {
            let result = 0;
            for (let perf of data.performance) {
                result += perf.volumeCredits;
            }
            return result;
            // return data.performance.reduce((total,p)=>{total+p.volumeCredits,0});
        }
    }
}